import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

import com.speljohan.rsbot.bot.Bot;
import com.speljohan.rsbot.event.listeners.PaintListener;
import com.speljohan.rsbot.script.Calculations;
import com.speljohan.rsbot.script.Constants;
import com.speljohan.rsbot.script.Script;
import com.speljohan.rsbot.script.wrappers.RSInterface;
import com.speljohan.rsbot.script.wrappers.RSNPC;
import com.speljohan.rsbot.script.wrappers.RSObject;
import com.speljohan.rsbot.script.wrappers.RSTile;

public class TehEssMiner extends Script implements PaintListener { // Names the
	// script
	JFrame f;
	JTable t;
	Hashtable<String, String> info;
	long startTime;
	long lastLog;
	RSTile lastTile;
	int lastCount;

	boolean debug = false;
	int pos = 4;
	long lastMine;
	int startingXp;
	long lastChange;

	RSTile[] portal = { new RSTile(2885, 4850), new RSTile(2889, 4813),
			new RSTile(2932, 4854), new RSTile(2933, 4815) };
	RSTile[] essLoc = { new RSTile(2893, 4849), new RSTile(2895, 4814),
			new RSTile(2927, 4850), new RSTile(2929, 4816) };
	int[] essHeight = { 70, 140, 140, 140 };
	double[] essDevX = { .5, .9, .6, .6 };
	double[] essDevY = { .5, .2, .6, .4 };
	int[] essWidth;
	RSTile bankTile = new RSTile(3253, 3421);
	RSTile aubTile = new RSTile(3253, 3400);
	int[] pickaxes = { 1265, 1267, 1269, 1296, 1273, 1271, 1275 };

	public int bank() {
		if (pos != 4) {
			if (getMyPlayer().isMoving()) {
				return random(50, 100);
			}
			if (atTile(portal[pos], "Use")) {
				return random(50, 100);
			}
			walkPathMM(randomizePath(fixPath2(portal[pos]), 2, 2), 17);
			if (distanceTo(portal[pos]) <= 7) {
				turnToTile(portal[pos]);
			}
			return random(50, 100);
		}
		if (bank.getInterface().isValid()) {
			bank.depositAllExcept(pickaxes);
			wait(random(50, 100));
			if (getInventoryCountExcept(pickaxes) == 0) {
				bank.close();
			}
			return random(50, 100);
		}
		final RSObject bank = getNearestObjectByID(11402);
		if (bank == null || !tileOnScreen(bank.getLocation())
				|| !getMyPlayer().isMoving() && !atObject(bank, "se-quickly")) {
			walkPathMM(randomizePath(fixPath(generateProperPath(bankTile)), 2,
					2), 17);
			if (!getMyPlayer().isMoving()
					&& (!tileOnScreen(bank.getLocation()) || random(0, 25) == 5)) {
				// just
				// in
				// case
				// weird
				// shit.
				if (distanceTo(bank.getLocation()) < 8) {
					turnToTile(bank.getLocation());
				} else {
					walkTileMM(randomizeTile(bankTile, 2, 2));
				}
			}
		} else {
			return random(2000, 3000);
		}
		return random(50, 100);
	}

	public Point circleTile(final int x, final int y, final double distance) {
		int angle = getAngleToCoordinates(x, y) - 180;
		if (angle < 0) {
			angle += 360;
		}
		final java.util.List<Double> possibleX = new ArrayList<Double>();
		final java.util.List<Double> possibleY = new ArrayList<Double>();
		final java.util.List<Integer> possibleHeight = new ArrayList<Integer>();
		int rawX;
		int rawY;
		double xDev;
		double yDev;
		int height;
		for (int i = angle - 20; i < angle + 20; i += 5) {
			int i2 = i;
			if (i < 0) {
				i2 = 360 - i;
			}
			final double distXTemp = Math.sin(i2) * distance;
			final double distYTemp = Math.cos(i2) * distance;
			final int rawXTemp = (int) Math.floor(distXTemp);
			final int rawYTemp = (int) Math.floor(distYTemp);
			final double xDevTemp = distXTemp - rawXTemp;
			final double yDevTemp = distYTemp - rawYTemp;
			final Point tempPoint = Calculations.tileToScreen(x + rawXTemp, y
					+ rawYTemp, xDevTemp, yDevTemp, 30);
			if (tempPoint != null && tempPoint.x != -1 && tempPoint.y != -1) {
				possibleX.add(distXTemp);
				possibleY.add(distYTemp);
			}
		}
		if (possibleX.isEmpty()) {
			// log("possibleX Empty");
			return null;
		}
		final int id = random(0, possibleX.size());
		rawX = (int) Math.floor(possibleX.get(id));
		rawY = (int) Math.floor(possibleY.get(id));
		xDev = possibleX.get(id) - rawX;
		yDev = possibleY.get(id) - rawY;

		for (int i = 10; i < 150; i += 5) {
			final Point tempPoint = Calculations.tileToScreen(x + rawX, y
					+ rawY, xDev, yDev, i);
			if (tempPoint != null && tempPoint.x != -1 && tempPoint.y != -1) {
				possibleHeight.add(i);
			}
		}
		if (possibleHeight.isEmpty()) {
			// log("possibleHeight empty");
			return null;
		}
		height = possibleHeight.get(random(0, possibleHeight.size()));
		// log("rawX: "+rawX+", rawY: "+rawY+", xDev: "+xDev+", yDev: "+yDev+", Height: "+height);
		return Calculations
				.tileToScreen(x + rawX, y + rawY, xDev, yDev, height);

	}

	public void debug(final String s) {
		if (debug == true) {
			log(s);
		}
	}

	@Override
	public RSTile[] fixPath(final RSTile[] path) {
		final ArrayList<RSTile> newPath = new ArrayList<RSTile>();
		for (int i = 0; i < path.length - 1; i++) {
			final RSTile[] temp1 = fixPath2(path[i], path[i + 1]);
			for (final RSTile element : temp1) {
				newPath.add(element);
			}
		}
		return newPath.toArray(new RSTile[newPath.size()]);
	}

	@Override
	public RSTile[] fixPath2(int startX, int startY, final int destinationX,
			final int destinationY) { // most credits to aftermath
		double dx, dy;
		final ArrayList<RSTile> list = new ArrayList<RSTile>();

		list.add(new RSTile(startX, startY));
		while (Math.hypot(destinationY - startY, destinationX - startX) > 8) {
			dx = destinationX - startX;
			dy = destinationY - startY;
			final int gamble = random(14, 17);
			while (Math.hypot(dx, dy) > gamble) {
				dx *= .95;
				dy *= .95;
			}
			startX += (int) dx;
			startY += (int) dy;
			list.add(new RSTile(startX, startY));
		}
		list.add(new RSTile(destinationX, destinationY));
		return list.toArray(new RSTile[list.size()]);

	}

	@Override
	public RSTile[] fixPath2(final RSTile tile) {
		return fixPath2(getMyPlayer().getLocation().getX(), getMyPlayer()
				.getLocation().getY(), tile.getX(), tile.getY());
	}

	@Override
	public RSTile[] fixPath2(final RSTile tile, final RSTile tile2) {
		return fixPath2(tile.getX(), tile.getY(), tile2.getX(), tile2.getY());
	}

	@Override
	public String getAuthor() { // Sets the Authors Name
		return "Mike_";
	}

	@Override
	public String getName() { // Sets the Scripts name
		return "Teh Ess Miner";
	}

	public int getPos() { // my lazy method to getting position in
		// mine/elsewhere.
		if (distanceTo(new RSTile(2910, 4832)) < 5) {
			return random(0, 4);
		}
		if (getPlane() != 0) {
			RSObject o = getNearestObjectByID(24353);
			if (o == null) {
				o = getNearestObjectByID(24349);
			}
			if (o == null) {
				log("We're lost! Logging out");
				return -1;
			}
			for (int i = 0; i < 7; i++) {
				if (atTile(new RSTile(3256, 3421), "Climb-down")) {
					return 4;
				}
			}
			log("We're lost! Logging out");
			return -1;
		}
		if (distanceTo(new RSTile(2910, 4832)) > 50) {
			return 4;
		}
		final int myX = getMyPlayer().getLocation().getX();
		final int myY = getMyPlayer().getLocation().getY();
		if (myX <= 2910) {
			if (myY >= 4832) {
				return 0; // NW
			} else {
				return 1;// SW
			}
		} else if (myY >= 4832) {
			return 2; // NE
		} else {
			return 3;// SE
		}
	}

	@Override
	public String getScriptCategory() { // Sets the category that the script
		// fit's into
		return "Mining"; // ex Woodcutting, Fishing, Money Making, Quests
	}

	@Override
	public String getScriptDescription() { // Shows a description of the script
		// inside the script dump area
		String html = "<html>";
		html += "<body>";
		html += "This script mines ess in varrock west bank/aubury. Start anywhere during the mining process. This script automatically uses run.";
		html += "</body>";
		html += "</html>";
		return html;
	}

	public String getTopText() {
		return getMenuItems().get(0).toLowerCase();
	}

	@Override
	public double getVersion() { // Sets the Version number of the script
		return 1.1;
	}

	@Override
	public int loop() { // This is the area of the script where you code out the
		// things you want the bot to do
		try {
			if (Bot.getClient().isMenuOpen()
					&& (!getMyPlayer().isMoving() || getDestination() != null)) {
				atMenu("Cancel");
			}
			if (System.currentTimeMillis() - lastLog > 10000) {
				updateGUI();
				lastLog = System.currentTimeMillis();
			}
			final int lastPos = pos;
			pos = getPos();
			if (pos != lastPos && debug) {
				log("Position: " + pos);
			}
			if (!getMyPlayer().getLocation().equals(lastTile)
					|| getMyPlayer().getAnimation() != -1
					|| lastCount != getInventoryCount()) {
				lastChange = System.currentTimeMillis();
			}
			lastCount = getInventoryCount();
			lastTile = getMyPlayer().getLocation();
			if (System.currentTimeMillis() - lastChange > 600000 && pos == 4) {
				log("Idle for 10 minutes, and we are not mining. We probably are lost. Logging out; Ran for "
						+ info.get("Time Running"));
				return -1;
			}
			if (getMyPlayer().getAnimation() != -1
					&& !getMyPlayer().isInCombat()) {
				lastMine = System.currentTimeMillis();
				return random(50, 100);
			}
			int energy = 0;
			if (RSInterface.getInterface(750).isValid()) {
				energy = Integer.parseInt(RSInterface.getInterface(750)
						.getChild(5).getText().trim());
			}
			if (getMyPlayer().isMoving() && distanceTo(getDestination()) > 5) {
				if (energy >= random(random(random(50, 99), 99), 99)) { // triple
					// random
					// for
					// chance
					// of
					// higher
					// number
					setRun(true);
					debug("Setting run on. Energy is " + getEnergy());
					return random(1000, 2000);
				}
				return random(50, 100);
			}
			wait(random(90, 120));
			if (pos == -1) {
				logout();
				return -1;
			}
			// if(true)
			// return random(5000,10000);
			if (isInventoryFull()) {
				return bank();
			}
			return mine();
		} catch (final Exception e) {
		}
		return random(50, 100);
	}

	public int mine() {
		if (bank.getInterface().isValid() && random(0, 2) == 0) {
			bank.close();
		}
		if (pos == 4) {
			debug("Finding Aubury");
			final RSNPC aub = getNearestNPCByName("Aubury");
			RSTile aubTemp = aubTile;

			if (aub != null) {
				aubTemp = aub.getLocation();
			}
			if (!canReach(aubTile, false)) {
				log("Can't reach aub. eek.");
				final RSTile openDoor = new RSTile(3253, 3398);
				final RSObject nDoor = getNearestObjectByID(24381);
				if (nDoor != null && nDoor.getLocation().equals(openDoor)) {
					if (atTile(openDoor, random(10, 30),
							0.2 + Math.random() * 0.8, 1, "Open")) {
						return random(1000, 2000);
					} else if (distanceTo(openDoor) < 7) {
						turnToTile(openDoor);
					}

				}
			}
			if (aub == null || !tileOnScreen(aub.getLocation())
					|| !getMyPlayer().isMoving()) {
				if (atNPC(aub, "Teleport")) {
					return random(2000, 4000);
				} else {
					walkPathMM(randomizePath(
							fixPath(generateProperPath(aubTemp)), 2, 2), 17);
				}
			}
			return random(50, 100);
		}

		Point location = Calculations.tileToScreen(essLoc[pos]);
		if (location == null || !pointOnScreen(location)) {
			walkPathMM(randomizePath(fixPath2(essLoc[pos]), 2, 2), 17);
			if (distanceTo(essLoc[pos]) < 9) {
				turnToTile(essLoc[pos]);
			}
			return random(50, 100);
		}
		getDestination();
		if (getMyPlayer().isMoving()) {
			return random(50, 100);
		}

		if (getMyPlayer().getAnimation() != -1
				|| System.currentTimeMillis() - lastMine < 3000) {
			return random(50, 100);
		}
		if (distanceTo(essLoc[pos]) < 8
				&& Math.abs(getCameraAngle() - getAngleToTile(essLoc[pos])) > 45) {
			setCameraAltitude(20);
			turnToTile(essLoc[pos]);
		}
		debug("Clicking \"Mine\". Last Check:"
				+ (System.currentTimeMillis() - lastMine));
		lastMine = System.currentTimeMillis();
		location = circleTile(essLoc[pos].getX(), essLoc[pos].getY(), Math
				.random() * .15 + 1.7);
		if (location == null || !pointOnScreen(location)) {
			return random(50, 100);
		}
		moveMouse(location, 2, 2);
		if (getTopText().contains("alk here")) {
		} else if (getTopText().contains("Mine")) {
			clickMouse(true);
		} else {
			clickMouse(false);
			atMenu("Mine Rune");
		}
		return random(1000, 2000);

	}

	@Override
	public void onFinish() { // Loads any commands to do when the script is
		// ended
		if (debug) {
			Bot.getEventManager().removeListener(PaintListener.class, this);
		}
		log("");
		log("");
		log("I urge you to give feedback on this script. If you would like, you can post a progress report, or you can share your experience with it.");
		log("Forum URL: http://www.rsbot.org/vb/showthread.php?t=18460");
		log("");
	}

	public void onRepaint(final Graphics g) {
	}

	@Override
	public boolean onStart(final Map<String, String> args) { // Loads any
		// commands
		// needed when the
		// script is started
		if (!isLoggedIn()) {
			log("You must be logged in to start this script so we can see how much mining xp you start with.");
			return false;
		}
		startingXp = skills.getCurrentSkillExp(Constants.STAT_MINING);
		pos = getPos();
		info = new Hashtable<String, String>();
		info.put("Mined", "0 Rune Essence");
		startTime = System.currentTimeMillis();
		showGUI();
		lastChange = System.currentTimeMillis();
		return true;
	}

	// ///GUI bullshit
	public void showGUI() {
		f = new JFrame(getName() + " v" + getVersion() + " -By " + getAuthor());
		t = new JTable(1, 2);
		f.setAlwaysOnTop(true);
		f
				.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
		f.getContentPane().add(t);
		f.pack();
		f.setSize(200, 400);
		f.setVisible(true);
	}

	public void updateGUI() {
		long millis = System.currentTimeMillis() - startTime;
		final long seconds2 = millis / 1000;
		final long hours = millis / (1000 * 60 * 60);
		millis -= hours * 1000 * 60 * 60;
		final long minutes = millis / (1000 * 60);
		millis -= minutes * 1000 * 60;
		final long seconds = millis / 1000;
		String hoursString = "";
		String minutesString = "";
		String secondsString = seconds + "";
		String type = "seconds";
		if (minutes > 0) {
			minutesString = minutes + ":";
			type = "minutes";
		}
		if (hours > 0) {
			hoursString = hours + ":";
			type = "hours";
		}
		if (minutes < 10 && hours > 0) {
			minutesString = "0" + minutesString;
		}
		if (seconds < 10 && minutes > 0) {
			secondsString = "0" + secondsString;
		}
		final int xpGained = skills.getCurrentSkillExp(Constants.STAT_MINING)
				- startingXp;
		final int essMined = xpGained / 5;
		if (seconds2 > 0) {
			if (info.contains("Ess/Hour")) {
				info.remove("Ess/Hour");
			}
			if (info.contains("Exp/Hour")) {
				info.remove("Exp/Hour");
			}
			info.put("Exp/Hour", "" + xpGained * 3600 / seconds2);
			info.put("Ess/Hour", "" + essMined * 3600 / seconds2);
		}
		info.remove("Rune Essence Mined");
		info.put("Mined", essMined + " Rune Essence");
		info.remove("Time Running");
		info.put("Time Running", "" + hoursString + minutesString
				+ secondsString + " " + type + "");

		updateTable();
	}

	public void updateTable() {
		t.setModel(new AbstractTableModel() {
			private static final long serialVersionUID = 1L;

			public int getColumnCount() {
				return 2;
			}

			@Override
			public String getColumnName(final int column) {
				if (column == 0) {
					return "Key";
				} else {
					return "Value";
				}
			}

			private String getKey(final int a_index) {
				String retval = "";
				final Enumeration<String> e = info.keys();
				for (int i = 0; i < a_index + 1; i++) {
					retval = e.nextElement();
				} // for

				return retval;
			}

			public int getRowCount() {
				return info.size();
			}

			public Object getValueAt(final int rowIndex, final int columnIndex) {
				if (columnIndex == 0) {
					return getKey(rowIndex);
				} else {
					return info.get(getKey(rowIndex));
				} // if-else

			}

		});
		// f.pack();
	}

}